﻿
Imports System.IO
Imports std
Imports storoomc

Public Class slice2
    Inherits _slice(Of _atom_base)

    Public Const datapathSuffix As String = ".data"
    Public Const filesystemEntitiesCountBit As UInt64 = 16
    Public Const filesystemEntitiesCount As UInt64 = 1 << filesystemEntitiesCountBit

    Public MustInherit Class _atom_base
        Public start As UInt64 = 0
        Public size As UInt64 = undefinedSize
        Public [end] As UInt64 = 0
        Private Shared ReadOnly undefinedSize As UInt64 = maxUInt64

        Public Function sizeUndefined() As Boolean
            Return size = undefinedSize
        End Function

        Public Function length() As UInt64
            Return [end] - start
        End Function

        Public MustOverride Sub createGUID()
    End Class

    Protected Class atom
        Inherits _atom_base
        Public guid As String = Nothing

        Public Overrides Sub createGUID()
            guid = Global.createGUID()
        End Sub
    End Class

    Protected Shared Function c(ByVal a As _atom_base) As atom
        Return convertor.cast(Of atom)(a)
    End Function

    Private Function datafile(ByVal atom As atom) As String
        Dim v As UInt32
        v = signing(atom.guid)
        Dim path As String = Nothing
        Dim file As String = Nothing
        path = Convert.ToString(v >> filesystemEntitiesCountBit)
        file = Convert.ToString(v And (filesystemEntitiesCount - 1))
        Dim fullpath As String = Nothing
        fullpath = combinepath(datarootpath(), path)
        createDirectory(fullpath)

        Return combinepath(fullpath, file)
    End Function

    Protected Overridable Function datafile(ByVal atom As _atom_base) As String
        Return datafile(c(atom))
    End Function

    Protected Overridable Function _atom_ctor() As _atom_base
        Return New atom()
    End Function

    Protected Overridable Function readguid(ByVal atom As _atom_base) As Boolean
        Return binaryReaderHelper.readString(reader(), c(atom).guid)
    End Function

    Protected Overridable Function datapathdepth() As Int32
        Return 1
    End Function

    Protected Function datarootpath() As String
        Static path As String = Nothing
        If path Is Nothing Then
            path = combinepath(datapath(), filename() + datapathSuffix)
            createDirectory(path)
        End If
        Return path
    End Function

    Private Function existDatafile(ByVal atom As _atom_base) As Boolean
        Return existDatafile(datafile(atom))
    End Function

    Private Shared Function existDatafile(ByVal fullfilename As String) As Boolean
        Return File.Exists(fullfilename)
    End Function

    Private Sub getatomsize(ByVal atom As _atom_base)
        If debugassert(Not atom Is Nothing, "atom is nothing.") Then
            Dim filename As String = Nothing
            filename = datafile(atom)
            startdiskoper()
            If File.Exists(filename) Then
                On Error Resume Next
                Dim info As FileInfo = Nothing
                info = New FileInfo(filename)

                If Not info Is Nothing Then
                    atom.size = info.Length()
                Else
                    raiseError("cannot get fileinfo " + filename + ", ignore.", errorHandle.errorType.exclamation)
                    atom.size = 0
                End If
            Else
                raiseError("unexpected not existing file " + filename, errorHandle.errorType.exclamation)
                atom.size = 0
            End If
            enddiskoper()
        End If
    End Sub

    Protected NotOverridable Overrides Sub open_start()
        MyBase.open_start()
        autoSizeOf()
    End Sub

    Private Sub autoSizeOfImpl()
        If Not busy() Then
            Dim action As traversal_atoms_action = Function(atom As _atom_base) As Boolean
                                                       Return _sizeofImpl(atom, Nothing) = ternary.true
                                                   End Function
            If lock_traversal_atoms(action) Then
                autoSizeOf(False)
            End If
        End If
    End Sub

    Private Sub autoSizeOf(Optional ByVal start As Boolean = True)
        Static timer3Guid As Guid = Guid.Empty
        If start Then
            If timer3Guid = Guid.Empty Then
                timer3Guid = timer3.addcall(AddressOf autoSizeOfImpl, 64)
            Else
                raiseError("should not call autoSizeOf(start) twice.", errorHandle.errorType.exclamation)
            End If
        ElseIf timer3Guid <> Guid.Empty Then
            debugassert(timer3.removecall(timer3Guid), _
                        "cannot remove timer3 call, guid " + Convert.ToString(timer3Guid))
            timer3Guid = Guid.Empty
        End If
    End Sub

    Protected NotOverridable Overrides Function appendImpl(ByVal atom As _atom_base, ByVal buff As bytes) As Boolean
        Dim filename As String = Nothing
        filename = datafile(atom)
        If Not existDatafile(filename) Then
            raiseError("unexpected not existing file " + filename + ", create.", _
                       errorHandle.errorType.exclamation)
            Return modifyImpl(atom, buff)
        End If
        Dim stream As Stream = Nothing
        Try
            stream = New FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read)
        Catch ex As Exception
            raiseError("failed to open file " + filename + ", ex " + ex.Message, errorHandle.errorType.exclamation)
            closestream(stream)
            Return False
        End Try

        Dim rtn As Boolean
        Try
            stream.Seek(0, SeekOrigin.End)
            stream.Write(buff, 0, buff.size())
            atom.size = stream.Length()
            rtn = True
        Catch ex As Exception
            raiseError("cannot write data to file " + filename + ", ex " + ex.Message, _
                       errorHandle.errorType.exclamation)
            rtn = False
        End Try

        closestream(stream)
        Return rtn
    End Function

    Protected NotOverridable Overrides Function atom_ctor() As _atom_base
        Dim rtn As _atom_base = Nothing
        rtn = _atom_ctor()
        rtn.createGUID()
        While existDatafile(rtn)
            raiseError("congratulations, the guid points to an existing file ... try to set another filename.", _
                       errorHandle.errorType.information)
            rtn.createGUID()
        End While
        rtn.start = stream().Length()
        Return rtn
    End Function

    Protected NotOverridable Overrides Function closeImpl() As Boolean
        Return True
    End Function

    Protected NotOverridable Overrides Function deleteImpl(ByVal atom As _atom_base) As Boolean
        Dim movelen As Int64
        movelen = stream().Length() - atom.end
        If movelen < 0 Then
            raiseError("cannot get enough data from slice " + filename() + ", invalid slice file.", _
                       errorHandle.errorType.exclamation)
            Return False
        ElseIf movelen > 0 Then
            If streammove(stream(), atom.end, atom.start, movelen) <> movelen Then
                raiseError("connot move enough data from index file " + filename(), _
                           errorHandle.errorType.exclamation)
                Return False
            End If

            Dim action As traversal_atoms_action = Function(a As _atom_base) As Boolean
                                                       If a.start > atom.start Then
                                                           a.start -= atom.length()
                                                           a.end -= atom.length()
                                                       End If
                                                       Return True
                                                   End Function
            traversal_atoms(action)
        End If
        stream().SetLength(atom.start + movelen)

        Dim datafilename As String = Nothing
        datafilename = datafile(atom)
        Try
            File.Delete(datafilename)
        Catch ex As Exception
            raiseError("cannot delete file " + datafile(atom), errorHandle.errorType.warning)
        End Try
        deleteEmptyDirectory(parentpath(datafilename), datapathdepth(), 4)

        Return True
    End Function

    Protected Overrides Function filter_slicetype_value() As String
        Return configNaming.filter.value.slicetype.slice2
    End Function

    Protected Overrides Function maxSupportVersion() As String
        Return "1"
    End Function

    Protected Overrides Function minSupportVersion() As String
        Return "1"
    End Function

    Protected NotOverridable Overrides Function modifyImpl(ByVal atom As _atom_base, ByVal buff As bytes) As Boolean
        Dim filename As String = Nothing
        filename = datafile(atom)
        Dim stream As Stream = Nothing
        Try
            stream = New FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read)
        Catch ex As Exception
            raiseError("failed to open file " + filename + ", ex " + ex.Message, errorHandle.errorType.exclamation)
            closestream(stream)
            Return False
        End Try

        Dim rtn As Boolean
        Try
            stream.Seek(0, SeekOrigin.Begin)
            stream.Write(buff, 0, buff.size())
            stream.SetLength(buff.size())
            atom.size = buff.size()
            rtn = True
        Catch ex As Exception
            raiseError("cannot write data to file " + filename + ", ex " + ex.Message, _
                       errorHandle.errorType.exclamation)
            rtn = False
        End Try

        closestream(stream)
        Return rtn
    End Function

    Protected NotOverridable Overrides Function readImpl(ByVal atom As _atom_base, ByRef buff As bytes) As Boolean
        Dim filename As String = Nothing
        filename = datafile(atom)
        If Not File.Exists(filename) Then
            raiseError("unexpected not exist file " + filename, errorHandle.errorType.exclamation)
            Return False
        Else
            Try
                buff = File.ReadAllBytes(filename)
                atom.size = buff.size()
                Return True
            Catch ex As Exception
                raiseError("cannot read data file " + filename + ", ex " + ex.Message, _
                           errorHandle.errorType.exclamation)
                Return False
            End Try
        End If
    End Function

    Protected NotOverridable Overrides Function readIndex(ByRef key As String, ByRef atom As _atom_base, _
                                                          ByVal lastpos As UInt64) As Boolean
        Dim rtn As _atom_base = Nothing
        rtn = _atom_ctor()
        If binaryReaderHelper.readString(reader(), key, retryTimes:=maxInt64) AndAlso _
           readguid(rtn) Then
            atom = rtn
            atom.start = lastpos
            atom.end = stream().Position()
            Return True
        Else
            Return False
        End If
    End Function

    Protected NotOverridable Overrides Function retireImpl() As Boolean
        Return create_empty_slice() AndAlso deleteDirectory(datarootpath()) AndAlso createDirectory(datarootpath())
    End Function

    Private Function _sizeofImpl(ByVal atom As _atom_base, ByRef size As UInt64) As ternary
        If debugassert(Not atom Is Nothing, "atom is nothing.") Then
            Dim rtn As ternary
            If atom.sizeUndefined() Then
                getatomsize(atom)
                rtn = ternary.unknown
            Else
                rtn = ternary.true
            End If
            size = atom.size
            Return rtn
        Else
            Return ternary.false
        End If
    End Function

    Protected NotOverridable Overrides Function sizeofImpl(ByVal atom As _atom_base, ByRef size As UInt64) As Boolean
        Return _sizeofImpl(atom, size) = ternary.true
    End Function

    Protected Overrides Function version() As String
        Return "1"
    End Function

    Protected Overridable Sub writeguid(ByVal a As _atom_base)
        writer().Write(c(a).guid)
    End Sub

    Protected NotOverridable Overrides Function writeImpl(ByVal key As String, ByVal atom As _atom_base, _
                                                          ByVal buff As bytes) As Boolean
        stream().Seek(0, SeekOrigin.End)
        writer().Write(key)
        writeguid(atom)
        atom.end = stream().Length()
        Dim filename As String = Nothing
        filename = datafile(atom)
        Try
            File.WriteAllBytes(filename, buff)
            atom.size = buff.size()
            Return True
        Catch ex As Exception
            raiseError("failed to write data to file " + filename + ", ex " + ex.Message, _
                       errorHandle.errorType.exclamation)
            Return False
        End Try
    End Function
End Class
